package trading.spring;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;


public class PropertyDirectoryPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

    private List<File> _propertyFiles;
    
    /**
     * Call {@link PropertyPlaceholderConfigurer#setLocations(Resource[])} for each
     * file found in a recursive search of propertiesDirectory that has the extension
     * ".properties".
     * 
     * <p>The order in which property files are loaded is undefined, so
     * don't define a property multiple times!
     * 
     * @param propertiesPath the path to the root directory containing properties files.
     * May contain "${foo}"-style references to system properties, to bootstrap
     */
    public void setPropertiesDirectory( String propertiesPath ) {
        propertiesPath = parseStringValue( propertiesPath, System.getProperties(), new HashSet() );
        File propertiesDirectory = new File( propertiesPath );
        
        // FIXME: this shouldn't be needed any more, but for backwards compatibility...
        System.setProperty("env.config.dir", propertiesDirectory.getAbsolutePath());
        
        if ( !propertiesDirectory.exists() ) {
            throw new RuntimeException("propertiesDirectory must exist (given '" + propertiesDirectory.getAbsolutePath() + "')");
        } else if ( !propertiesDirectory.isDirectory() ) {
            throw new RuntimeException("propertiesDirectory must be a directory (given '" + propertiesDirectory.getAbsolutePath() + "')");
        } else if ( !propertiesDirectory.canRead() ) {
            throw new RuntimeException("propertiesDirectory must be readable (given '" + propertiesDirectory.getAbsolutePath() + "')");
        }
        
        _propertyFiles = getPropertiesFiles( propertiesDirectory );
        
        List<Resource> resources = new ArrayList<Resource>( _propertyFiles.size() );
        for ( File propertyFile: _propertyFiles ) {
            resources.add( new FileSystemResource(propertyFile) );
        }
        setLocations(resources.toArray(new Resource[] {}));
    }
    

    /**
     * Return a List of Files for each file whose name ends in ".properties" in the
     * given directory. Assumes that the directory exists and is a directory. Processes
     * directories recursively.
     * @param rootDir the directory to load .properties files from
     * @return the List of File objects found in the rootDir 
     */
    private List<File> getPropertiesFiles( File rootDir ) {
        List<File> propertiesFiles = new ArrayList<File>();
        for ( File candidate: rootDir.listFiles() ) {
            if ( candidate.canRead() ) {
                if ( candidate.isFile() && candidate.getName().endsWith(".properties") ) {
                    propertiesFiles.add( candidate );
                } else if ( candidate.isDirectory() ) {
                    List<File> recursive = getPropertiesFiles( candidate );
                    propertiesFiles.addAll( recursive );
                }
            }
        }
        
        return propertiesFiles;
    }

    
    /**
     * Override the default method to, in addition to resolving property placeholders
     * in the bean definitions (ie XML files), sets system properties for each property
     * that was loaded from the properties files.
     * 
     * @see PropertyPlaceholderConfigurer#processProperties(ConfigurableListableBeanFactory, Properties)
     */
    @SuppressWarnings("unchecked")
    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) {
        super.processProperties(beanFactoryToProcess, props);
        
        Map<String, String> propertyMap = new HashMap<String, String>();
        try {
            Properties mergedProps = mergeProperties();
            Enumeration<String> propertyNames = (Enumeration<String>)mergedProps.propertyNames();
            while ( propertyNames.hasMoreElements() ) {
                String name = propertyNames.nextElement();
                String value = mergedProps.getProperty(name);
                value = parseStringValue(value, mergedProps, new HashSet());
                propertyMap.put(name, value);
            }
        } catch ( IOException ignored ) {
            // this would have been from the mergeProperties()
            // call above. We do sufficient checks before setting
            // the property files to ensure that this exception
            // is never raised. famous last words, i know. ~ dcrosta
        }
        
        for ( Map.Entry<String, String> entry: propertyMap.entrySet() ) {
            System.setProperty(entry.getKey(), entry.getValue());
        }
    }
}
